﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System;
using System.Xml.Serialization;
using BrainTechLLC;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections;
using System.ComponentModel;
#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC.ThreadSafeObjects
{
    /// <summary>
    /// A thread safe list & lookup allows multiple threads to efficiently work
    /// with both a single list for Adding, Removing, and Searching for lists of items by key
    /// This version is not 100% safe using the FindSingle() method for collections that frequently remove/add items
    /// </summary>
#if NO_SILVERLIGHT
    [TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
#endif
    [Serializable]
    [DataContract]
    public class ThreadSafeListAndLookup<TIndex, TListType> :
        Lockable,
        IMultipleItems<TListType>,
        ISupportsFindMany<TIndex, TListType>,
        ISupportsCount,
        INotifyCollectionChanged,
        ISupportsAddRemoveClear<TIndex, TListType> where TListType : class
    {
        public ThreadSafeListAndLookup() { }
        public ThreadSafeListAndLookup(IEnumerable<TListType> items, Func<TListType, TIndex> getKeys)
        {
            foreach (TListType item in items)
            {
                Add(getKeys(item), item);
            }
        }

        public override string ToString()
        {
            return _count.ToString();
        }

        public IEnumerator GetEnumerator()
        {
            return AllItems.GetEnumerator();
        }
        /// <summary>
        /// Stores a running count of items
        /// </summary>
        [DataMember]
        public int _count;

        /// <summary>
        /// A full list of items in the collection
        /// </summary>
        [DataMember]
        public List<TListType> _list = new List<TListType>();

        /// <summary>
        /// A dictionary of list of items, use for fast lookup by index/key
        /// </summary>
        [DataMember]
        public Dictionary<TIndex, List<TListType>> Lookup = new Dictionary<TIndex, List<TListType>>();

        /// <summary>
        /// Stores a common (cached) empty list
        /// </summary>
        [NonSerialized, XmlIgnore]
        protected readonly static List<TListType> _emptyList = new List<TListType>();

        public virtual object Clone() { return MakeCopy(); }

        protected virtual ThreadSafeListAndLookup<TIndex, TListType> MakeCopy() { throw new NotImplementedException(); }

        /// <summary>
        /// Thread-safe (but somewhat expensive) method for getting an item by index.  Returns null if the indexed item does not exists
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        public TListType GetItem(int n)
        {
            TListType item = null;

            AquireLock();
            {
                if (n < _count)
                {
                    item = _list[n];
                }
            }
            ReleaseLock();

            return item;
        }

        /// <summary>
        /// Returns an array containing all items in the collection
        /// </summary>
        public TListType[] ArrayOfItems { get { return AllItems.ToArray(); } }

        /// <summary>
        /// Returns a mostly-accurate count of items currently in the collection
        /// </summary>
        public int Count { get { return _count; } }

        /// <summary>
        /// Inserts an item into the list at the specified sub-list index
        /// </summary>
        /// <param name="index"></param>
        /// <param name="item"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public virtual bool Insert(int index, TListType item, TIndex key)
        {
            List<TListType> list;
            bool found = true;

            AquireLock();
            {
                if (!Lookup.TryGetValue(key, out list))
                {
                    list = new List<TListType>();
                    Lookup.Add(key, list);
                }

                if (!list.Contains(item))
                {
                    list.Add(item);
                    _list.Insert(index, item);
                    _count++;
                    found = false;
                }
            }
            ReleaseLock();

            if (!found)
            {
                OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
            }
            return !found;
        }

        /// <summary>
        /// Adds an item to the collection
        /// </summary>
        /// <returns>True if the item was already in the collection, otherwise false</returns>
        public virtual bool Add(TIndex key, TListType item)
        {
            List<TListType> list;
            bool found = true;

            AquireLock();
            {
                if (!Lookup.TryGetValue(key, out list))
                {
                    list = new List<TListType>();
                    Lookup.Add(key, list);
                }

                if (!list.Contains(item))
                {
                    list.Add(item);
                    _list.Add(item);
                    _count++;
                    found = false;
                }
            }
            ReleaseLock();

            if (!found)
            {
                OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
            }
            return !found;
        }

        public virtual bool AddMultiple(TIndex key, List<TListType> items)
        {
            List<TListType> list;
            bool found = true;

            AquireLock();
            {
                if (!Lookup.TryGetValue(key, out list))
                {
                    list = new List<TListType>();
                    Lookup.Add(key, list);
                }

                list.AddRange(items);
                _list.AddRange(items);
                _count += items.Count;
                found = false;
            }
            ReleaseLock();

            if (!found)
            {
                OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, items);
            }
            return !found;
        }

        /// <summary>
        /// Finds the item, or adds the item if it doesn't exist in the collection
        /// </summary>
        /// <param name="key"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public virtual TListType FindOrAdd(TIndex key, TListType item)
        {
            List<TListType> list;
            TListType foundItem;
            bool found = true;

            AquireLock();
            {
                if (!Lookup.TryGetValue(key, out list))
                {
                    list = new List<TListType>();
                    Lookup.Add(key, list);
                }

                int index = list.IndexOf(item);

                if (index < 0)
                {
                    list.Add(item);
                    _list.Add(item);
                    _count++;
                    found = false;
                    foundItem = item;
                }
                else
                {
                    foundItem = list[index];
                }
            }
            ReleaseLock();

            if (!found)
            {
                OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
            }

            return foundItem;
        }

        /// <summary>
        /// Obtains the list with key 'key'
        /// Searches the list using match(item)
        /// If the item is not found, adds the item using the return value of createFunc()
        /// </summary>
        /// <param name="key"></param>
        /// <param name="match"></param>
        /// <param name="createFunc"></param>
        /// <returns></returns>
        public virtual TListType FindOrAdd(TIndex key, Func<TListType, bool> match, Func<TListType> createFunc)
        {
            List<TListType> list;
            TListType foundItem;
            bool found = true;

            AquireLock();
            {
                if (!Lookup.TryGetValue(key, out list))
                {
                    list = new List<TListType>();
                    Lookup.Add(key, list);
                }

                int index = -1;

                for (int n = 0; n < list.Count; n++)
                {
                    if (match(list[n]))
                    {
                        index = n;
                        break;
                    }
                }

                if (index < 0)
                {
                    foundItem = createFunc();
                    list.Add(foundItem);
                    _list.Add(foundItem);
                    _count++;
                    found = false;
                }
                else
                {
                    foundItem = list[index];
                }
            }
            ReleaseLock();

            if (!found)
            {
                OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, foundItem);
            }

            return foundItem;
        }


        /// <summary>
        /// Returns a snapshot/list copy of all items in the collection
        /// </summary>
        public List<TListType> AllItems
        {
            get
            {
                List<TListType> list = new List<TListType>(_count);

                AquireLock();
                {
                    list.AddRange(_list);
                }
                ReleaseLock();

                return list;
            }
        }

        /// <summary>
        /// Find a keyed list
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public List<TListType> this[TIndex key]
        {
            get { return Find(key); }
        }

        /// <summary>
        /// Finds the first item in the collection with the given key
        /// </summary>
        public virtual TListType FindSingle(TIndex key)
        {
            TListType item = null;
            List<TListType> list;

            // Note - this is potentially thread-unsafe if many items are being added/removed
            if (Lookup.TryGetValue(key, out list) && list.Count > 0)
                item = list[0];

            return item;
        }

        /// <summary>
        /// Removes an item from the collection
        /// </summary>
        /// <returns>True if the item was removed, false if the item could not be found in the collection</returns>
        public bool Remove(TIndex key, TListType item)
        {
            if (!Lookup.ContainsKey(key))
                return false;

            List<TListType> list;

            AquireLock();
            {
                if (Lookup.TryGetValue(key, out list))
                {
                    list.Remove(item);
                }
                if (_list.Remove(item))
                {
                    _count--;
                }
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, item);

            return true;
        }

        /// <summary>
        /// Removes a single item from the collection based on a key and a match delegate
        /// </summary>
        public TListType Remove(TIndex key, Func<TListType, bool> match)
        {
            if (!Lookup.ContainsKey(key))
                return null;

            List<TListType> list;
            List<TListType> removed = null;
            TListType found = null;

            if (CollectionChanged != null)
                removed = new List<TListType>();

            AquireLock();
            {
                if (Lookup.TryGetValue(key, out list))
                {
                    for (int n = 0; n < list.Count; n++)
                    {
                        TListType item = list[n];
                        if (match(item))
                        {
                            if (removed != null) removed.Add(item);
                            list.RemoveAt(n);
                            break;
                        }
                    }
                }

                for (int n = 0; n < _list.Count; n++)
                {
                    TListType item = _list[n];
                    if (match(item))
                    {
                        found = item;
                        _list.RemoveAt(n);
                        _count--;
                        break;
                    }
                }
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, removed);
            return found;
        }

        public TListType RemoveAll(TIndex key, Func<TListType, bool> match)
        {
            if (!Lookup.ContainsKey(key))
                return null;

            List<TListType> list;
            List<TListType> removed = null;
            TListType found = null;

            if (CollectionChanged != null)
                removed = new List<TListType>();

            AquireLock();
            {
                if (Lookup.TryGetValue(key, out list))
                {
                    for (int n = list.Count - 1; n >= 0; n--)
                    {
                        TListType item = list[n];
                        if (match(item))
                        {
                            if (removed != null) removed.Add(item);
                            list.RemoveAt(n);
                        }
                    }
                }

                for (int n = _list.Count - 1; n >= 0; n--)
                {
                    TListType item = _list[n];
                    if (match(item))
                    {
                        found = item;
                        _list.RemoveAt(n);
                        _count--;
                    }
                }
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, removed);
            return found;
        }

        /// <summary>
        /// Removes a single item from the collection based on a key and a match delegate
        /// </summary>
        public List<TListType> Remove(Func<TListType, bool> match)
        {
            List<TListType> removed = new List<TListType>();

            AquireLock();
            {
                foreach (var kvp in Lookup)
                {
                    for (int n = kvp.Value.Count - 1; n >= 0; n--)
                    {
                        if (match(kvp.Value[n]))
                        {
                            removed.Add(kvp.Value[n]);
                            kvp.Value.RemoveAt(n);
                        }
                    }
                }

                for (int n = _list.Count - 1; n >= 0; n--)
                {
                    TListType item = _list[n];
                    if (match(item))
                    {
                        _list.RemoveAt(n);
                        _count--;
                    }
                }
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, removed);
            return removed;
        }

        /// <summary>
        /// Clears all items from the collection
        /// </summary>
        public void Clear()
        {
            List<TListType> list = null;

            if (CollectionChanged != null)
                list = new List<TListType>(AllItems);

            AquireLock();
            {
                _list.Clear();
                Lookup.Clear();
                _count = 0;
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, list);

        }

        /// <summary>
        /// Finds all items in the collection with the given key
        /// </summary>
        public List<TListType> Find(TIndex key)
        {
            List<TListType> list;
            if (!Lookup.ContainsKey(key))
            {
                return _emptyList;
            }
            else
            {
                AquireLock();
                {
                    if (Lookup.TryGetValue(key, out list))
                    {
                        list = new List<TListType>(list);
                    }
                    else
                    {
                        list = _emptyList;
                    }
                }
                ReleaseLock();

                return list;
            }
        }

        /// <summary>
        /// Finds a single item in the collection with the given key, using a match delegate
        /// </summary>
        public TListType Find(TIndex key, Func<TListType, bool> match)
        {
            List<TListType> list;
            if (!Lookup.ContainsKey(key))
                return null;

            TListType item = null;

            AquireLock();
            {
                if (Lookup.TryGetValue(key, out list))
                {
                    for (int n = 0; n < list.Count; n++)
                    {
                        TListType find = list[n];
                        if (match(find))
                        {
                            item = find;
                            break;
                        }
                    }
                }
            }
            ReleaseLock();

            return item;
        }

        public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, IList<TListType> changedItems)
        {
#if SILVERLIGHT
#else
            if (CollectionChanged != null)
            {
                switch (action)
                {
                    case NotifyCollectionChangedAction.Add:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action, changedItems));
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action, changedItems));
                        break;
                    case NotifyCollectionChangedAction.Replace:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action, changedItems[0], changedItems[1]));
                        break;
                    case NotifyCollectionChangedAction.Reset:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action));
                        break;
                }
            }
#endif
        }

        public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, TListType changedItem)
        {
            if (CollectionChanged != null)
                OnNotifyCollectionChanged(action, new List<TListType>() { changedItem });
        }

        #region INotifyCollectionChanged Members

        [field: NonSerialized]
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        #endregion
    }

#if NO_SILVERLIGHT
    [TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
#endif
    [Serializable]
    [DataContract]
#if NO_SILVERLIGHT
    [Browsable(true)]
    [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
#if NO_SILVERLIGHT
    [EditorBrowsable(EditorBrowsableState.Always)]
#endif
    public class ThreadSafeListAndLookupNonRef<TIndex, TListType> :
         Lockable,
         ISupportsCount
    {
        public override string ToString()
        {
            return _count.ToString();
        }

        public IEnumerator GetEnumerator()
        {
            return AllItems.GetEnumerator();
        }
        /// <summary>
        /// Stores a running count of items
        /// </summary>
        [DataMember]
        public int _count;

        /// <summary>
        /// A full list of items in the collection
        /// </summary>
        [DataMember]
        public List<TListType> _list = new List<TListType>();

        /// <summary>
        /// A dictionary of list of items, use for fast lookup by index/key
        /// </summary>
        [DataMember]
        public Dictionary<TIndex, List<TListType>> Lookup = new Dictionary<TIndex, List<TListType>>();

        /// <summary>
        /// Stores a common (cached) empty list
        /// </summary>
        [NonSerialized, XmlIgnore]
        protected readonly static List<TListType> _emptyList = new List<TListType>();

        public object Clone() { return MakeCopy(); }

        /// <summary>
        /// Thread-safe (but somewhat expensive) method for getting an item by index.  Returns null if the indexed item does not exists
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        public TListType GetItem(int n)
        {
            TListType item = default(TListType);

            AquireLock();
            {
                if (n < _count)
                {
                    item = _list[n];
                }
            }
            ReleaseLock();

            return item;
        }

        /// <summary>
        /// Returns an array containing all items in the collection
        /// </summary>
        public TListType[] ArrayOfItems { get { return AllItems.ToArray(); } }

        /// <summary>
        /// Returns a mostly-accurate count of items currently in the collection
        /// </summary>
        public int Count { get { return _count; } }

        public virtual bool Insert(int index, TListType item, TIndex key)
        {
            List<TListType> list;
            bool found = true;

            AquireLock();
            {
                if (!Lookup.TryGetValue(key, out list))
                {
                    list = new List<TListType>();
                    Lookup.Add(key, list);
                }

                if (!list.Contains(item))
                {
                    list.Add(item);
                    _list.Insert(index, item);
                    _count++;
                    found = false;
                }
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
            return !found;
        }

        /// <summary>
        /// Adds an item to the collection
        /// </summary>
        /// <returns>True if the item was already in the collection, otherwise false</returns>
        public virtual bool Add(TIndex key, TListType item)
        {
            List<TListType> list;
            bool found = true;

            AquireLock();
            {
                if (!Lookup.TryGetValue(key, out list))
                {
                    list = new List<TListType>();
                    Lookup.Add(key, list);
                }

                if (!list.Contains(item))
                {
                    list.Add(item);
                    _list.Add(item);
                    _count++;
                    found = false;
                }
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item);
            return !found;
        }

        /// <summary>
        /// Returns all keys for lists in the collection
        /// </summary>
        public List<TIndex> Keys
        {
            get
            {
                List<TIndex> items = new List<TIndex>(Lookup.Count);
                AquireLock(); { Lookup.ForEach(kvp => items.Add(kvp.Key)); } ReleaseLock();
                return items;
            }
        }

        /// <summary>
        /// Adds multiple items according to their corresponding item keys
        /// </summary>
        /// <param name="itemKeys"></param>
        /// <param name="items"></param>
        /// <returns></returns>
        public virtual bool AddMultiple(IList<TIndex> itemKeys, IList<TListType> items)
        {
            List<TListType> list;
            bool found = true;

            AquireLock();
            {
                for (int n = 0; n < itemKeys.Count; n++)
                {
                    TIndex key = itemKeys[n];
                    TListType item = items[n];

                    if (!Lookup.TryGetValue(key, out list))
                    {
                        list = new List<TListType>();
                        Lookup.Add(key, list);
                    }

                    if (!list.Contains(item))
                    {
                        list.Add(item);
                        _list.Add(item);
                        _count++;
                        found = false;
                    }
                }
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, items);
            return !found;
        }

        /// <summary>
        /// Returns a snapshot/list copy of all items in the collection
        /// </summary>
        public List<TListType> AllItems
        {
            get
            {
                List<TListType> list = new List<TListType>(_count);

                AquireLock();
                {
                    list.AddRange(_list);
                }
                ReleaseLock();

                return list;
            }
        }

        /// <summary>
        /// Finds the first item in the collection with the given key
        /// </summary>
        public virtual TListType FindSingle(TIndex key)
        {
            TListType item = default(TListType);
            List<TListType> list;

            // Note - this is potentially thread-unsafe if many items are being added/removed
            if (Lookup.TryGetValue(key, out list) && list.Count > 0)
                item = list[0];

            return item;
        }

        /// <summary>
        /// Removes an item from the collection
        /// </summary>
        /// <returns>True if the item was removed, false if the item could not be found in the collection</returns>
        public bool Remove(TIndex key, TListType item)
        {
            if (!Lookup.ContainsKey(key))
                return false;

            List<TListType> list;

            AquireLock();
            {
                if (Lookup.TryGetValue(key, out list))
                {
                    list.Remove(item);
                }
                if (_list.Remove(item))
                {
                    _count--;
                }
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, item);
            return true;
        }

        /// <summary>
        /// Removes a single item from the collection based on a key and a match delegate
        /// </summary>
        public TListType Remove(TIndex key, Func<TListType, bool> match)
        {
            if (!Lookup.ContainsKey(key))
                return default(TListType);

            List<TListType> list;
            TListType found = default(TListType);

            AquireLock();
            {
                if (Lookup.TryGetValue(key, out list))
                {
                    for (int n = 0; n < list.Count; n++)
                    {
                        TListType item = list[n];
                        if (match(item))
                        {
                            list.RemoveAt(n);
                            break;
                        }
                    }
                }

                for (int n = 0; n < _list.Count; n++)
                {
                    TListType item = _list[n];
                    if (match(item))
                    {
                        found = item;
                        _list.RemoveAt(n);
                        _count--;
                        break;
                    }
                }
            }
            ReleaseLock();

            if (found != null)
                OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, found);

            return found;
        }

        /// <summary>
        /// Clears all items from the collection
        /// </summary>
        public void Clear()
        {
            List<TListType> list = null;

            if (CollectionChanged != null)
                list = new List<TListType>(AllItems);

            AquireLock();
            {
                _list.Clear();
                Lookup.Clear();
                _count = 0;
            }
            ReleaseLock();

            OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, list);
        }

        /// <summary>
        /// Finds all items in the collection with the given key
        /// </summary>
        public List<TListType> Find(TIndex key)
        {
            List<TListType> list;
            if (!Lookup.ContainsKey(key))
            {
                return _emptyList;
            }
            else
            {
                AquireLock();
                {
                    if (Lookup.TryGetValue(key, out list))
                    {
                        list = new List<TListType>(list);
                    }
                    else
                    {
                        list = _emptyList;
                    }
                }
                ReleaseLock();

                return list;
            }
        }

        /// <summary>
        /// Finds a single item in the collection with the given key, using a match delegate
        /// </summary>
        public TListType Find(TIndex key, Func<TListType, bool> match)
        {
            List<TListType> list;
            if (!Lookup.ContainsKey(key))
                return default(TListType);

            TListType item = default(TListType);

            AquireLock();
            {
                if (Lookup.TryGetValue(key, out list))
                {
                    for (int n = 0; n < list.Count; n++)
                    {
                        TListType find = list[n];
                        if (match(find))
                        {
                            item = find;
                            break;
                        }
                    }
                }
                ReleaseLock();

                return item;
            }
        }

        protected virtual ThreadSafeListAndLookupNonRef<TIndex, TListType> MakeCopy() { throw new Exception("Not supported"); }

        public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, IList<TListType> changedItems)
        {
#if SILVERLIGHT
#else
            if (CollectionChanged != null)
            {
                switch (action)
                {
                    case NotifyCollectionChangedAction.Add:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action, changedItems));
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action, changedItems));
                        break;
                    case NotifyCollectionChangedAction.Replace:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action, changedItems[0], changedItems[1]));
                        break;
                    case NotifyCollectionChangedAction.Reset:
                        CollectionChanged(this, new NotifyCollectionChangedEventArgsEx<TListType>(action));
                        break;
                }
            }
#endif
        }

        public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, TListType changedItem)
        {
            if (CollectionChanged != null)
                OnNotifyCollectionChanged(action, new List<TListType>() { changedItem });
        }

        #region INotifyCollectionChanged Members

        [field: NonSerialized]
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        #endregion
    }

    [Serializable]
    [DataContract]
#if NO_SILVERLIGHT
    [TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
#endif
    public class ThreadSafeListAndLookupBase<TIndex, TListType> : ThreadSafeListAndLookup<TIndex, TListType> where TListType : class
    {
        protected override ThreadSafeListAndLookup<TIndex, TListType> MakeCopy() { throw new Exception("Not supported"); }
    }
}